package com.huonan.utils;

import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.UnknownHostException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Map;
import javax.net.ssl.SSLException;
import org.apache.calcite.avatica.org.apache.commons.logging.LogFactory;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpRequest;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.protocol.HttpContext;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

/**
 * @Description TODO
 * @Author huonan
 * @Date 2021/2/8 11:15 上午
 * @Version V1.0
 **/
public class HttpUtils {
    private static final Logger log = LogManager.getLogger("HttpUtils");
    private final int MAX_CONN = 60;               // max connection
    private final int MAX_PER_ROUTE = 30;
    private static final int CONNECT_TIMEOUT = 90 * 1000;   // 设置连接超时时间为10秒
    private static final int SOCKET_TIMEOUT = 180 * 1000;    // 设置请求获取数据的超时时间为60秒

    private SSLConnectionSocketFactory sslsf;
    private PoolingHttpClientConnectionManager cm;

    private HttpUtils() {
        try {
            SSLContextBuilder builder = new SSLContextBuilder();
            // 全部信任 不做身份鉴定
            builder.loadTrustMaterial(null, new TrustStrategy() {
                @Override
                public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
                    return true;
                }
            });
            this.sslsf = new SSLConnectionSocketFactory(
                    builder.build(),
                    new String[]{"SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.2"},
                    null,
                    NoopHostnameVerifier.INSTANCE);
            Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
                    .register("http", new PlainConnectionSocketFactory())
                    .register("https", sslsf)
                    .build();
            this.cm = new PoolingHttpClientConnectionManager(registry);
            this.cm.setMaxTotal(MAX_CONN);
            this.cm.setDefaultMaxPerRoute(MAX_PER_ROUTE);
        } catch (Exception e) {
            log.error("init http utils failed!");
            e.printStackTrace();
        }
    }

    private static class Holder {
        private static HttpUtils httpUtils = new HttpUtils();
    }

    private CloseableHttpClient client() {
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectTimeout(CONNECT_TIMEOUT)
                .setSocketTimeout(SOCKET_TIMEOUT)
                .build();
        HttpRequestRetryHandler retryHandler = new HttpRequestRetryHandler() {
            @Override
            public boolean retryRequest(
                    IOException exception,
                    int executionCount,
                    HttpContext context) {
                if (executionCount >= 5) {
                    // Do not retry if over max retry count
                    return false;
                }
                if (exception instanceof InterruptedIOException             // Timeout
                        || exception instanceof UnknownHostException        // Unknown host
                        || exception instanceof ConnectTimeoutException     // Connection refused
                        || exception instanceof SSLException                // SSL handshake exception
                ) {
                    return false;
                }
                HttpClientContext clientContext = HttpClientContext.adapt(context);
                HttpRequest request = clientContext.getRequest();
                boolean idempotent = !(request instanceof HttpEntityEnclosingRequest);
                if (idempotent) {
                    // Retry if the request is considered idempotent
                    return true;
                }
                return false;
            }
        };
        return HttpClientBuilder.create()
                .setDefaultRequestConfig(requestConfig)
                .setRetryHandler(retryHandler)
                .setSSLSocketFactory(sslsf)
                .setConnectionManager(cm)
                .setConnectionManagerShared(true)
                .build();
    }

    /**
     * @param url
     * @param header
     * @return
     * @throws IOException
     */
    private String get(String url, HashMap<String, String> header) throws IOException {
        CloseableHttpClient client = null;
        CloseableHttpResponse response = null;
        try {
            client = client();
            HttpGet request = new HttpGet(url);
            // set header
            if (header != null && header.size() > 0) {
                for (Map.Entry<String, String> entry : header.entrySet()) {
                    request.setHeader(entry.getKey(), entry.getValue());
                }
            }
            response = client.execute(request);
            String content = EntityUtils.toString(response.getEntity());
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode >= 400) {
                log.error(String.format("%s\t%s\t%s", statusCode, url, content));
            }
            return content;
        } finally {
            if (response != null) {
                response.close();
            }
        }
    }

    private String put(String url,HashMap<String, String> header,String body) throws IOException {
        CloseableHttpClient client = null;
        CloseableHttpResponse response = null;
        try {
            client = client();
            HttpPut request = new HttpPut(url);
            // set header
            if (header != null && header.size() > 0) {
                for (Map.Entry<String, String> entry : header.entrySet()) {
                    request.setHeader(entry.getKey(), entry.getValue());
                }
            }
            StringEntity entity = new StringEntity(body, ContentType.APPLICATION_JSON);
            request.setEntity(entity);
            response = client.execute(request);
            String content = EntityUtils.toString(response.getEntity(), ContentType.APPLICATION_JSON.getCharset());
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode >= 400) {
                log.error(String.format("%s\t%s\t%s", statusCode, url, content));
            }
            return content;
        } finally {
            if (response != null) {
                response.close();
            }
        }
    }
    /**
     * @param url
     * @return
     * @throws IOException
     */
    private CloseableHttpResponse getResponse(String url) throws IOException {
        CloseableHttpClient client;
        CloseableHttpResponse response;
        HashMap<String, String> header = header();
        HttpGet request = new HttpGet(url);
        if (header != null && header.size() > 0) {
            for (Map.Entry<String, String> entry : header.entrySet()) {
                request.setHeader(entry.getKey(), entry.getValue());
            }
        }
        client = client();
        response = client.execute(request);
        return response;

    }

    /**
     * @param url    请求url
     * @param header 头部信息
     * @param body   请求实体
     * @return 响应结果
     * @throws IOException
     */
    private String post(String url, HashMap<String, String> header, String body, ContentType contentType)
            throws IOException {
        CloseableHttpClient client = null;
        CloseableHttpResponse response = null;
        try {
            client = client();
            HttpPost request = new HttpPost(url);
            // set header
            if (header != null && header.size() > 0) {
                for (Map.Entry<String, String> entry : header.entrySet()) {
                    request.setHeader(entry.getKey(), entry.getValue());
                }
            }
            StringEntity entity = new StringEntity(body, contentType);
            request.setEntity(entity);
            response = client.execute(request);
            String content = EntityUtils.toString(response.getEntity(), contentType.getCharset());
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode >= 400) {
                log.error(String.format("%s\t%s\t%s", statusCode, url, content));
            }
            return content;
        } finally {
            if (response != null) {
                response.close();
            }
        }
    }

    private String post2(String url, HashMap<String, String> header, String body, ContentType contentType)
            throws IOException {
        CloseableHttpClient client = null;
        CloseableHttpResponse response = null;
        try {
            client = client();
            HttpPost httpPost = new HttpPost(url);
            // set header
            if (header != null && header.size() > 0) {
                for (Map.Entry<String, String> entry : header.entrySet()) {
                    httpPost.setHeader(entry.getKey(), entry.getValue());
                }
            }
            StringEntity entity = new StringEntity(body, contentType);
            httpPost.setEntity(entity);
            //构建超时等配置信息
            RequestConfig config = RequestConfig.custom().setConnectTimeout(600*1000) //连接超时时间
                    .setConnectionRequestTimeout(600*1000) //从连接池中取的连接的最长时间
                    .setSocketTimeout(600*1000) //数据传输的超时时间
                    .setStaleConnectionCheckEnabled(true) //提交请求前测试连接是否可用
                    .build();
            //设置请求配置时间
            httpPost.setConfig(config);
            response = client.execute(httpPost);
            String content = EntityUtils.toString(response.getEntity(), contentType.getCharset());
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode >= 400) {
                log.error(String.format("%s\t%s\t%s", statusCode, url, content));
            }
            return content;
        } finally {
            if (response != null) {
                response.close();
            }
        }
    }

    public static HashMap<String, String> header() {
        HashMap<String, String> header = new HashMap<>();
        header.put("accept", "*/*");
        header.put("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
        return header;
    }

    public static String get(String url) throws IOException {
        HttpUtils httpUtils = Holder.httpUtils;
        return httpUtils.get(url, header());
    }

    public static CloseableHttpResponse getOriginalResponse(String url) throws IOException {
        HttpUtils httpUtils = Holder.httpUtils;
        return httpUtils.getResponse(url);
    }

    public static String get(HashMap<String, String> header, String url) throws IOException {
        HttpUtils httpUtils = Holder.httpUtils;
        return httpUtils.get(url, header);
    }

    public static String fpost(String url, String body) throws IOException {
        HttpUtils httpUtils = Holder.httpUtils;
        return httpUtils.post(url, header(), body, ContentType.APPLICATION_JSON);
    }

    public static String put(String url, String body) throws IOException {
        HttpUtils httpUtils = Holder.httpUtils;
        return httpUtils.put(url, header(), body);
    }

    public static String post(String url, String body, HashMap<String, String> header) throws IOException {
        // 设置默认工厂类
        System.setProperty("org.apache.commons.logging.LogFactory", "org.apache.commons.logging.impl.LogFactoryImpl");
        // 设置日志打印类
        LogFactory.getFactory().setAttribute("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.SimpleLog");
        //设置默认日志级别
        LogFactory.getFactory().setAttribute("org.apache.commons.logging.simplelog.defaultlog", "error");

        HttpUtils httpUtils = Holder.httpUtils;
        return httpUtils.post2(url, header, body, ContentType.APPLICATION_JSON);
    }

    public static String postForm(String url, String body) throws IOException {
        HttpUtils httpUtils = Holder.httpUtils;
        return httpUtils.post(url, header(), body, ContentType.APPLICATION_JSON);
    }

    public static String postForm(String url, String body, HashMap<String, String> header) throws IOException {
        HttpUtils httpUtils = Holder.httpUtils;
        return httpUtils.post(url, header, body, ContentType.APPLICATION_FORM_URLENCODED);
    }
}
